{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "# Implementing Self-Refine technique using Large Language Models"
      ],
      "metadata": {
        "id": "EMXSKrtNDIW9"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Installing the dependencies"
      ],
      "metadata": {
        "id": "4eRlQWS4DO1o"
      }
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "collapsed": true,
        "id": "ahMjmxKV9Ddy",
        "outputId": "6107616f-3961-4f0f-ea05-e4270209611c"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Collecting mirascope[openai]\n",
            "  Downloading mirascope-1.25.4-py3-none-any.whl.metadata (8.5 kB)\n",
            "Requirement already satisfied: docstring-parser<1.0,>=0.15 in /usr/local/lib/python3.11/dist-packages (from mirascope[openai]) (0.17.0)\n",
            "Requirement already satisfied: jiter>=0.5.0 in /usr/local/lib/python3.11/dist-packages (from mirascope[openai]) (0.10.0)\n",
            "Requirement already satisfied: pydantic<3.0,>=2.7.4 in /usr/local/lib/python3.11/dist-packages (from mirascope[openai]) (2.11.7)\n",
            "Requirement already satisfied: typing-extensions>=4.10.0 in /usr/local/lib/python3.11/dist-packages (from mirascope[openai]) (4.14.1)\n",
            "Requirement already satisfied: openai<2,>=1.6.0 in /usr/local/lib/python3.11/dist-packages (from mirascope[openai]) (1.97.1)\n",
            "Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.11/dist-packages (from openai<2,>=1.6.0->mirascope[openai]) (4.9.0)\n",
            "Requirement already satisfied: distro<2,>=1.7.0 in /usr/local/lib/python3.11/dist-packages (from openai<2,>=1.6.0->mirascope[openai]) (1.9.0)\n",
            "Requirement already satisfied: httpx<1,>=0.23.0 in /usr/local/lib/python3.11/dist-packages (from openai<2,>=1.6.0->mirascope[openai]) (0.28.1)\n",
            "Requirement already satisfied: sniffio in /usr/local/lib/python3.11/dist-packages (from openai<2,>=1.6.0->mirascope[openai]) (1.3.1)\n",
            "Requirement already satisfied: tqdm>4 in /usr/local/lib/python3.11/dist-packages (from openai<2,>=1.6.0->mirascope[openai]) (4.67.1)\n",
            "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.11/dist-packages (from pydantic<3.0,>=2.7.4->mirascope[openai]) (0.7.0)\n",
            "Requirement already satisfied: pydantic-core==2.33.2 in /usr/local/lib/python3.11/dist-packages (from pydantic<3.0,>=2.7.4->mirascope[openai]) (2.33.2)\n",
            "Requirement already satisfied: typing-inspection>=0.4.0 in /usr/local/lib/python3.11/dist-packages (from pydantic<3.0,>=2.7.4->mirascope[openai]) (0.4.1)\n",
            "Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.11/dist-packages (from anyio<5,>=3.5.0->openai<2,>=1.6.0->mirascope[openai]) (3.10)\n",
            "Requirement already satisfied: certifi in /usr/local/lib/python3.11/dist-packages (from httpx<1,>=0.23.0->openai<2,>=1.6.0->mirascope[openai]) (2025.7.14)\n",
            "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.11/dist-packages (from httpx<1,>=0.23.0->openai<2,>=1.6.0->mirascope[openai]) (1.0.9)\n",
            "Requirement already satisfied: h11>=0.16 in /usr/local/lib/python3.11/dist-packages (from httpcore==1.*->httpx<1,>=0.23.0->openai<2,>=1.6.0->mirascope[openai]) (0.16.0)\n",
            "Downloading mirascope-1.25.4-py3-none-any.whl (373 kB)\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m373.2/373.2 kB\u001b[0m \u001b[31m5.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25hInstalling collected packages: mirascope\n",
            "Successfully installed mirascope-1.25.4\n"
          ]
        }
      ],
      "source": [
        "!pip install \"mirascope[openai]\""
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "## OpenAI API Key\n",
        "To get an OpenAI API key, visit https://platform.openai.com/settings/organization/api-keys and generate a new key. If you're a new user, you may need to add billing details and make a minimum payment of $5 to activate API access."
      ],
      "metadata": {
        "id": "5EFUcFABGJ5R"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "import os\n",
        "from getpass import getpass\n",
        "os.environ[\"OPENAI_API_KEY\"] = getpass('Enter OpenAI API Key: ')"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "gCOQtGk3AQXn",
        "outputId": "a22b97c7-0b1f-4bc3-b292-0c8d09d898a4"
      },
      "execution_count": 1,
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Enter OpenAI API Key: ··········\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Basic Self-Refine Implementation\n",
        "This is a basic self-refine implementation using Mirascope and OpenAI’s gpt-4o-mini model. It starts by generating an initial answer to a math problem, then evaluates the response using structured feedback. That feedback is used to generate a revised answer, allowing iterative improvement. The refinement depth is configurable, making it suitable for tasks that benefit from multiple review cycles."
      ],
      "metadata": {
        "id": "Ir9hS9uQDWlA"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from mirascope.core import openai, prompt_template\n",
        "from mirascope.core.openai import OpenAICallResponse\n",
        "\n",
        "\n",
        "@openai.call(model=\"gpt-4o-mini\")\n",
        "def call(query: str) -> str:\n",
        "    return query\n",
        "\n",
        "\n",
        "@openai.call(model=\"gpt-4o-mini\")\n",
        "@prompt_template(\n",
        "    \"\"\"\n",
        "    Here is a query and a response to the query. Give feedback about the answer,\n",
        "    noting what was correct and incorrect.\n",
        "    Query:\n",
        "    {query}\n",
        "    Response:\n",
        "    {response}\n",
        "    \"\"\"\n",
        ")\n",
        "def evaluate_response(query: str, response: OpenAICallResponse): ...\n",
        "\n",
        "\n",
        "@openai.call(model=\"gpt-4o-mini\")\n",
        "@prompt_template(\n",
        "    \"\"\"\n",
        "    For this query:\n",
        "    {query}\n",
        "    The following response was given:\n",
        "    {response}\n",
        "    Here is some feedback about the response:\n",
        "    {feedback}\n",
        "\n",
        "    Consider the feedback to generate a new response to the query.\n",
        "    \"\"\"\n",
        ")\n",
        "def generate_new_response(\n",
        "    query: str, response: OpenAICallResponse\n",
        ") -> openai.OpenAIDynamicConfig:\n",
        "    feedback = evaluate_response(query, response)\n",
        "    return {\"computed_fields\": {\"feedback\": feedback}}\n",
        "\n",
        "\n",
        "def self_refine(query: str, depth: int) -> str:\n",
        "    response = call(query)\n",
        "    for _ in range(depth):\n",
        "        response = generate_new_response(query, response)\n",
        "    return response.content\n",
        "\n",
        "\n",
        "query = \"A train travels 120 km at a certain speed. If the speed had been 20 km/h faster, it would have taken 30 minutes less to cover the same distance. What was the original speed of the train?\"\n",
        "\n",
        "print(self_refine(query, 1))"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "1fWfG44yARBE",
        "outputId": "955022ef-aad2-4500-e0b9-9200cc2ac752"
      },
      "execution_count": 8,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Let's start over with a clean and structured approach to the problem, ensuring clarity in each step while also emphasizing the importance of the conclusions drawn.\n",
            "\n",
            "---\n",
            "\n",
            "**Problem Statement:**\n",
            "\n",
            "A train travels 120 km at a certain speed. If the speed had been 20 km/h faster, it would have taken 30 minutes less to cover the same distance. What was the original speed of the train?\n",
            "\n",
            "**Solution:**\n",
            "\n",
            "Let the original speed of the train be \\( v \\) km/h. \n",
            "\n",
            "1. **Calculate the time taken at the original speed:**\n",
            "   \\[\n",
            "   \\text{Time at original speed} = \\frac{120}{v} \\text{ hours}\n",
            "   \\]\n",
            "\n",
            "2. **Calculate the time taken at the faster speed:**\n",
            "   If the speed had been 20 km/h faster, the new speed is \\( v + 20 \\) km/h. Therefore, the time taken to cover 120 km at this speed is:\n",
            "   \\[\n",
            "   \\text{Time at faster speed} = \\frac{120}{v + 20} \\text{ hours}\n",
            "   \\]\n",
            "\n",
            "3. **Establish the time difference:**\n",
            "   According to the problem, the difference in time between traveling at the original speed and the faster speed is 30 minutes, which is equivalent to \\( \\frac{1}{2} \\) hours. We set up the equation:\n",
            "   \\[\n",
            "   \\frac{120}{v} - \\frac{120}{v + 20} = \\frac{1}{2}\n",
            "   \\]\n",
            "\n",
            "4. **Eliminate the fractions:**\n",
            "   To eliminate the fractions, we multiply through by \\( 2v(v + 20) \\):\n",
            "   \\[\n",
            "   2v(v + 20) \\left( \\frac{120}{v} - \\frac{120}{v + 20} \\right) = 2v(v + 20) \\cdot \\frac{1}{2}\n",
            "   \\]\n",
            "   Simplifying this results in:\n",
            "   \\[\n",
            "   240(v + 20) - 240v = v(v + 20)\n",
            "   \\]\n",
            "\n",
            "5. **Simplify the equation:**\n",
            "   \\[\n",
            "   240v + 4800 - 240v = v^2 + 20v\n",
            "   \\]\n",
            "   This simplifies to:\n",
            "   \\[\n",
            "   4800 = v^2 + 20v\n",
            "   \\]\n",
            "\n",
            "6. **Rearranging to form a quadratic equation:**\n",
            "   We rearrange the equation to standard form:\n",
            "   \\[\n",
            "   v^2 + 20v - 4800 = 0\n",
            "   \\]\n",
            "\n",
            "7. **Using the quadratic formula:**\n",
            "   We will solve the quadratic equation using the formula:\n",
            "   \\[\n",
            "   v = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}\n",
            "   \\]\n",
            "   where \\( a = 1 \\), \\( b = 20 \\), and \\( c = -4800 \\).\n",
            "\n",
            "   First, calculate the discriminant:\n",
            "   \\[\n",
            "   b^2 - 4ac = 20^2 - 4 \\cdot 1 \\cdot (-4800) = 400 + 19200 = 19600\n",
            "   \\]\n",
            "   Now substitute into the formula:\n",
            "   \\[\n",
            "   v = \\frac{-20 \\pm \\sqrt{19600}}{2}\n",
            "   \\]\n",
            "   Calculate the square root:\n",
            "   \\[\n",
            "   \\sqrt{19600} = 140\n",
            "   \\]\n",
            "   Now we have:\n",
            "   \\[\n",
            "   v = \\frac{-20 \\pm 140}{2}\n",
            "   \\]\n",
            "\n",
            "8. **Calculating the possible speeds:**\n",
            "   This gives us two potential solutions:\n",
            "   \\[\n",
            "   v = \\frac{120}{2} = 60\n",
            "   \\]\n",
            "   \\[\n",
            "   v = \\frac{-160}{2} = -80 \\quad \\text{(not valid since speed cannot be negative)}\n",
            "   \\]\n",
            "\n",
            "**Conclusion:**\n",
            "The original speed of the train is:\n",
            "\\[\n",
            "\\boxed{60} \\text{ km/h}\n",
            "\\]\n",
            "\n",
            "This structured approach provides clarity at each step, ensuring that the calculations and reasoning are easy to follow. It highlights the importance of eliminating negative speeds in the context provided.\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Enhanced Self-Refine with Response Model\n",
        "This is an enhanced self-refine implementation tailored for solving math problems using Mirascope and Pydantic. Instead of returning a raw string, it structures the output using a MathSolution model that clearly separates the step-by-step reasoning from the final numerical answer. After generating an initial response, the system evaluates its quality and uses structured feedback to produce a refined, model-conformant solution. This approach improves clarity, enables downstream validation, and supports more reliable multi-step reasoning in mathematical tasks."
      ],
      "metadata": {
        "id": "qSuyZrM7Dupw"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from pydantic import BaseModel, Field\n",
        "\n",
        "\n",
        "class MathSolution(BaseModel):\n",
        "    steps: list[str] = Field(..., description=\"The steps taken to solve the problem\")\n",
        "    final_answer: float = Field(..., description=\"The final numerical answer\")\n",
        "\n",
        "\n",
        "@openai.call(model=\"gpt-4o-mini\", response_model=MathSolution)\n",
        "@prompt_template(\n",
        "    \"\"\"\n",
        "    For this query:\n",
        "    {query}\n",
        "    The following response was given:\n",
        "    {response}\n",
        "    Here is some feedback about the response:\n",
        "    {feedback}\n",
        "\n",
        "    Consider the feedback to generate a new response to the query.\n",
        "    Provide the solution steps and the final numerical answer.\n",
        "    \"\"\"\n",
        ")\n",
        "def enhanced_generate_new_response(\n",
        "    query: str, response: OpenAICallResponse\n",
        ") -> openai.OpenAIDynamicConfig:\n",
        "    feedback = evaluate_response(query, response)\n",
        "    return {\"computed_fields\": {\"feedback\": feedback}}\n",
        "\n",
        "\n",
        "def enhanced_self_refine(query: str, depth: int) -> MathSolution:\n",
        "    response = call(query)\n",
        "    for _ in range(depth):\n",
        "        solution = enhanced_generate_new_response(query, response)\n",
        "        response = f\"Steps: {solution.steps}\\nFinal Answer: {solution.final_answer}\"\n",
        "    return solution\n",
        "\n",
        "\n",
        "# Example usage\n",
        "result = enhanced_self_refine(query, 1)\n",
        "print(result)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "6Daq9kPIAdDf",
        "outputId": "f8189f6b-b0c6-4d06-dacc-5bc539cf2f9a"
      },
      "execution_count": 9,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "steps=['Let the original speed of the train be v km/h.', 'The time taken to travel 120 km at this speed is Time = 120/v hours.', 'With a speed of (v + 20) km/h, the time taken is Time = 120/(v + 20) hours.', 'The difference in time taken is set as 30 minutes or 0.5 hours: 120/v - 120/(v + 20) = 0.5.', 'Clearing the fractions by multiplying through by 2v(v + 20): 2v(v + 20)(120/v - 120/(v + 20)) = 2v(v + 20)(0.5).', 'This simplifies to: 240(v + 20) - 240v = v(v + 20).', 'Resulting in: 240v + 4800 - 240v = v^2 + 20v, which simplifies to 4800 = v^2 + 20v.', 'Rearranging gives the quadratic equation: v^2 + 20v - 4800 = 0.', 'Applying the quadratic formula v = (-b ± √(b^2 - 4ac)) / 2a with a = 1, b = 20, c = -4800.', 'Calculating the discriminant: 20^2 - 4(1)(-4800) = 400 + 19200 = 19600.', 'Finding v values: v = (-20 ± √19600) / 2 = (-20 ± 140) / 2.', 'The valid solution: v = (120) / 2 = 60 km/h.'] final_answer=60.0\n"
          ]
        }
      ]
    }
  ]
}